//=============================================================================
// SentinelShockCannon - Does combos.
//=============================================================================
class SentinelShockCannon extends SentinelWeapon;

var() class<ShockBeamEffect> BeamEffectClass;

var() float TraceRange;
var() int Damage;
var() float Momentum;
var() class<DamageType> DamageType;

var() sound BeamFireSound;

var ShockProjectile ComboTarget;
var float ComboDist, LastComboDist;

//So the Sentinel knows when it can fire.
function float MaxRange()
{
	return TraceRange;
}

function Projectile FireAt(vector Start, vector BarrelDir, vector Aim)
{
	Start += vect(0, 6, 0) >> rotator(Aim);
	ComboTarget = ShockProjectile(Super.FireAt(Start, BarrelDir, Aim));

	if(ComboTarget != None)
		GotoState('WaitForCombo');

	return ComboTarget;
}

function Projectile DoCombo(vector Start, vector BarrelDir, vector Aim)
{
	local Vector HitLocation, HitNormal;
	local Actor A;
	local ShockBeamEffect Beam;

	if(AmmoAmount < 1)
		return None;

	AmmoAmount -= AmmoPerFire;

	SetTeam();
	PlaySound(BeamFireSound);
	Sentinel.MakeNoise(Loudness);
	FlashCount++;

	if(Level.NetMode != NM_DedicatedServer)
		FlashMuzzleFlash();

	foreach TraceActors(class'Actor', A, HitLocation, HitNormal, Start + Aim * TraceRange, Start)
	{
		if(A == None)
			continue;

		if(A.bWorldGeometry)
			break;
		if(BigShieldSphere(A)!=none && BigShieldSphere(A).Shield.myteam==sentinel.myteam)
		  continue;

		//Don't hit own shield or instigator or self.
		if(A == Sentinel || (A.Owner == Sentinel && !A.IsA('Projectile')) || A == Sentinel.Instigator)
			continue;

		if(!A.bProjTarget)
			continue;

		A.TakeDamage(Damage, Sentinel.Instigator, HitLocation, Momentum * Aim, DamageType);
		break;
	}

	Start += (BarrelDir * ProjSpawnOffset) - (vect(0, 6, 0) >> rotator(Aim));

	Beam = Spawn(BeamEffectClass,,, Start, rotator(HitLocation - Start));
	Beam.Instigator = None;
	Beam.AimAt(HitLocation, HitNormal);

	return None;
}

state WaitForCombo
{
	function BeginState()
	{
		SentinelController(Sentinel.Controller).bForceTarget = true;
		Sentinel.Controller.Target = ComboTarget;
		ProjectileClass = None; //So the controller knows not to aim ahead.

		ComboDist = VSize(Sentinel.Location - Sentinel.Target.Location);
		SetTimer(0.1, true);
	}

	function EndState()
	{
		SentinelController(Sentinel.Controller).bForceTarget = false;
		ProjectileClass = Default.ProjectileClass;
	}

	//TODO: Maybe do a trace to set ComboDist, otherwise big pawns may hit the projectile before coming into combo range.
	function Timer()
	{
		local Controller C;

		if(ComboTarget == None)
		{
			GotoState('');
			return;
		}

		//If current target is no longer targetable but there is something else in range, do a combo right away.
		if(!SentinelController(Sentinel.Controller).PossiblyTarget(Sentinel.Controller.Enemy))
		{
			//Chance that Sentinel will try to combo regardless (may get enemy around corner).
			if(Sentinel.Controller.Enemy != None && VSize(ComboTarget.Location - Sentinel.Controller.Enemy.Location) < ComboTarget.ComboRadius && Frand() < 0.5)
				Sentinel.FireAt(ComboTarget.Location);
			else
			{
				for(C = Level.ControllerList; C != None; C = C.NextController)
				{
					if(C.Pawn != None && VSize(C.Pawn.Location - ComboTarget.Location) < ComboTarget.ComboRadius)
					{
						if(SentinelController(Sentinel.Controller).PossiblyTarget(C.Pawn))
							Sentinel.FireAt(ComboTarget.Location);
					}
				}
			}

			GotoState('');
			return;
		}

		Sentinel.Controller.Target = ComboTarget;

		LastComboDist = ComboDist;
		ComboDist = VSize(ComboTarget.Location - Sentinel.Target.Location);

		//Not in range yet or too close.
		if(ComboDist > ComboTarget.ComboRadius || VSize(ComboTarget.Location - Sentinel.Location) < 650)
			return;

		//Missed, darn.
		if(VSize(ComboTarget.Location - Sentinel.Location) > VSize(Sentinel.Target.Location - Sentinel.Location) + ComboTarget.ComboRadius)
		{
			GotoState('');
			return;
		}

		//Combo if target is moving away, or if close enough to get good damage.
		if(LastComboDist < ComboDist || ComboDist < ComboTarget.ComboRadius * 0.5)
			Sentinel.FireAt(ComboTarget.Location);

		if(ComboTarget == None)
			GotoState('');
	}

	function Projectile FireAt(vector Start, vector BarrelDir, vector Aim)
	{
		DoCombo(Start, BarrelDir, Aim);
		return None;
	}
}

defaultproperties
{
     BeamEffectClass=Class'XWeapons.ShockBeamEffect'
     TraceRange=10000.000000
     Damage=20
     Momentum=60000.000000
     DamageType=Class'taskforces.DamTypeSentinelShockBeam'
     BeamFireSound=SoundGroup'WeaponSounds.ShockRifle.ShockRifleFire'
     FireRate=1.000000
     FireSound=SoundGroup'WeaponSounds.ShockRifle.ShockRifleAltFire'
     ProjectileClass=Class'taskforces.SentinelShockProjectile'
     ProjSpawnOffset=70.000000
     WeaponAttachmentClass=Class'taskforces.SSRAttachment'
     WeaponOffset=(X=480.000000,Y=85.000000)
     Loudness=1.000000
     MaxAmmo=1
     AmmoAmount=1
     AmmoCost=1
     UpgradeName="Shock Cannon"
     Description="Based on ASMD Shock-Rifle technology licensed from Liandri Corp. The shock cannon has an unlimited supply of ammo thanks to the powerful shock-core.||Care must be taken in its employment due to it having a minimum effective range. In the right circumstances it is a very powerful weapon."
     ActivateSound=Sound'WeaponSounds.ShockRifle.SwitchToShockRifle'
}
